package com.zy.common.utils;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.Security;

/**
 * 国密SM4分组密码算法工具类（对称加密）
 * <p>GB/T 32907-2016 信息安全技术 SM4分组密码算法</p>
 *
 * @author BBF
 * @see <a href="http://www.gb688.cn/bzgk/gb/newGbInfo?hcno=7803DE42D3BC5E80B0C3E5D8E873D56A">GB/T
 * 32907-2016</a>
 */
public class Sm4Utils {

    private static final String ALGORITHM_NAME = "SM4";
    private static final String ALGORITHM_ECB_PKCS5PADDING = "SM4/ECB/PKCS5Padding";

    private static final String [] ENCRY_KEY = {
            "347B020CF645D98F7209E779402E2470",
            "FEDC9B72C8C3379A4F7C32A946B745F0",
            "910E83E710F262C3F924DB9153D12597",
            "B952C565D338C8C35D82160651110CDC",
            "77634E76F179241E4CEFBFBA1147DCDA",
            "699897FF849DF3422D107112F893D0F5",
            "38B1CA13DDAE26CC3A5B8E8BF0F475B4",
            "F7C1999652FA4AEBB4AFC83AB055FCA0",
            "9CDD9A07B64B432650699A8793BD5829",
            "C5B1787857F5807DFC1463BED3FE1DFC"
    };

    /**
     * SM4算法目前只支持128位（即密钥16字节）
     */
    private static final int DEFAULT_KEY_SIZE = 128;

    static {
        // 防止内存中出现多次BouncyCastleProvider的实例
        if (null == Security.getProvider(BouncyCastleProvider.PROVIDER_NAME)) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    private Sm4Utils() {
    }

    /**
     * 生成密钥
     * <p>建议使用binToHex将二进制转成HEX字符串</p>
     *
     * @return 密钥16位
     * @throws Exception 生成密钥异常
     */
    public static byte[] generateKey() throws Exception {
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
        kg.init(DEFAULT_KEY_SIZE, new SecureRandom());
        return kg.generateKey().getEncoded();
    }

    /**
     * Hex转byte[]，两种情况，Hex长度为奇数最后一个字符会被舍去
     */
    public static byte[] hexTobytes(String hex) {
        if (hex.length() < 1) {
            return null;
        } else {
            byte[] result = new byte[hex.length() / 2];
            int j = 0;
            for(int i = 0; i < hex.length(); i+=2) {
                result[j++] = (byte)Integer.parseInt(hex.substring(i,i+2), 16);
            }
            return result;
        }
    }

    /**
     * 加密，SM4-ECB-PKCS5Padding
     *
     * @param data 要加密的明文
     * @param key  密钥16字节，使用Sm4Util.generateKey()生成
     * @return 加密后的密文
     * @throws Exception 加密异常
     */
    public static byte[] encryptEcbPkcs5Padding(byte[] data, byte[] key) throws Exception {
        return sm4(data, key, ALGORITHM_ECB_PKCS5PADDING, null, Cipher.ENCRYPT_MODE);
    }

    /**
     * 解密，SM4-ECB-PKCS5Padding
     *
     * @param data 要解密的密文
     * @param key  密钥16字节，使用Sm4Util.generateKey()生成
     * @return 解密后的明文
     * @throws Exception 解密异常
     */
    public static byte[] decryptEcbPkcs5Padding(byte[] data, byte[] key) throws Exception {
        return sm4(data, key, ALGORITHM_ECB_PKCS5PADDING, null, Cipher.DECRYPT_MODE);
    }

    /**
     * SM4对称加解密
     *
     * @param input   明文（加密模式）或密文（解密模式）
     * @param key     密钥
     * @param sm4mode sm4加密模式
     * @param iv      初始向量(ECB模式下传NULL)
     * @param mode    Cipher.ENCRYPT_MODE - 加密；Cipher.DECRYPT_MODE - 解密
     * @return 密文（加密模式）或明文（解密模式）
     * @throws Exception 加解密异常
     */
    private static byte[] sm4(byte[] input, byte[] key, String sm4mode, byte[] iv, int mode)
            throws Exception {
        IvParameterSpec ivParameterSpec = null;
        if (null != iv) {
            ivParameterSpec = new IvParameterSpec(iv);
        }
        SecretKeySpec sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
        Cipher cipher = Cipher.getInstance(sm4mode, BouncyCastleProvider.PROVIDER_NAME);
        if (null == ivParameterSpec) {
            cipher.init(mode, sm4Key);
        } else {
            cipher.init(mode, sm4Key, ivParameterSpec);
        }
        return cipher.doFinal(input);
    }

    /**
     * SM4对称加密
     *
     * @param inputText   明文
     * @return 密文
     * @throws Exception 加解密异常
     */
    public static String encode(String inputText,int encryKeyIndex){
        byte[] output = new byte[0];
        try {
            output = Sm4Utils.encryptEcbPkcs5Padding(inputText.getBytes(StandardCharsets.UTF_8), hexTobytes(ENCRY_KEY[encryKeyIndex]));
        } catch (Exception e) {
            e.printStackTrace();
        }
        String hex = Hex.toHexString(output);
        return hex;
    }

    /**
     * SM4对称解密
     *
     * @param inputText 明文
     * @return 密文
     * @throws Exception 加解密异常
     */
    public static String decode(String inputText,int encryKeyIndex) {
        byte[] input = Hex.decode(inputText);
        byte[] output = new byte[0];
        try {
            output = Sm4Utils.decryptEcbPkcs5Padding(input, hexTobytes(ENCRY_KEY[encryKeyIndex]));
        } catch (Exception e) {
            return inputText;
        }
        String result = new String(output, StandardCharsets.UTF_8);
        return result;
    }

    /**
     * 字节数组转十六进制字符串
     *
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    public static String binToHex(byte[] bytes) {
        return Hex.toHexString(bytes).toUpperCase();
    }

    public static void main(String[] args) {
        String str = "b5c4856eeab81a78b9473a13c91b011e778d8ace15e1cd85531e36a6d64ffb16c812d0a8d5cfbb8047a8d620e3d4c8e85026f09e8187af0ed186f30e8cc546eaa9b34d6a7b88694ea582aab9a1678fc23ba3b98351b15c642dc194124db96a0dc32afc5d084263702c2b5a6f6ed6466d8426caca8705dca87499caeb08ad8ed206c58256a6f4458f1af381dfdf755c849181a3e2b074b4f84076f55ceab507f00c14c05afebb69d2969ca6b3d6423d155048615ccdd05604fe6094ddd1cc079c98dd4142501174a49aee4367eab17f9c6a93a01cc5531221a9347ab90e5d39294495fb85b96dd7ce6c149e5fed58bf2576b43a4044a0886adea018d68926f02e84a99f13a395d676e248ce2bebb91f5379a068df049a8e43cda5a3cab608457d5b7d189c793a9d64c559ec2ec28bd1dd829c051067fca56735b9785d6c862a978859e0d7a64a60577d66520069a12bacd197d25323fb4d573986014d6c0d03bcd7480ebf2094d4b3982c72b3ba61482d9002150703d89d267b6ea233b18cf96ba09332b71a7164869b93708e4ca759d3c812d0a8d5cfbb8047a8d620e3d4c8e85026f09e8187af0ed186f30e8cc546eaa9b34d6a7b88694ea582aab9a1678fc20425500dcb696f367eaf298e47c5d5d5516681716e3cf5d960abd5efaae727d56f35ccfdb3fa9e26d0d7a55f62d4231f990a0c4a3cfd05f3e1e94cad5e4ca31c0d5504264fdde9de1c5e4cc452679f1f4f3faa5a3031f465356b2d26eb900663c7ac16831454b7b2c85a918a9f403b7cae4419a9a553848a5a16b940847f63b30d49a2413232a44abed11ec8eaa2ab0d7121099aec62c4eafd9c4ede873bc97d25238e16667755222c2bd0d532783ca7ec02f2a01871cf8f6cda4a3b01b479f80a52f2f3270d65404fe20fb8caadf3db6583a7c5e24ada755cb20705d19e3f830bd76fceb716d3ba772c9cac83281b23fa642a2c249caede3a4c2a56787838a36001ebc85229f0c38e79235dee3fffeda5bd8c27c2aa77b9e69c095d3670d80706a25b70bef480b4ef61ced1b47ef60e3b9c5594e03cd9320dae4333e921d3e0c20396f78de20a0cf5bd5451f9f7fdec206111ea08b5223cd2cdedf66364991cedffb30dd26c4d9a3e67e1c20f6c24b27260744c326ea772db66323bd4e89ef330da673ef601da7c176ec37642bcafacc1cf4288eb04102314467eccdab7512d964ac52e023f4cde8b7023f7b57b580fd48b92b2ece7b08547e21527e4a5b5cb";
        System.out.println(Sm4Utils.decode(str,0));
    }
}
